use darling::{FromDeriveInput, FromMeta};
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, DeriveInput};
use proc_macro2::TokenStream as T2;

use crate::periph2::rcc::{self, RccAxB};

use super::time_delay::Timer;



#[derive(Debug, FromDeriveInput)]
#[darling(attributes(pwm))]
pub struct PwmConfig {
    tim: Timer,
    freq: u32,
    init: String,

    #[darling(default)]
    set_freq: bool,

    #[darling(default)]
    enable: bool,

    #[darling(default)]
    disable: bool,
    
    #[darling(multiple)]
    ch: Vec<PwmAttr>,

    #[darling(default)]
    irq: bool,
}

#[derive(Debug, FromMeta)]
pub struct PwmAttr {
    ch: u8,
    duty: u8,

    #[darling(default)]
    set_duty: bool,

    mode: u8,
}

impl PwmConfig {

    fn build(&self) -> T2 {
        let name = self.tim.name();
        let rcc_field = format_ident!("{}en", name);
        let init_fn = format_ident!("{}", self.init);
        let name = format_ident!("{}", name.to_uppercase());
        let rcc_src = if self.tim.is_advanced() {
            rcc::rcc_enable(RccAxB::APB2, &rcc_field, false)
        } else {
            rcc::rcc_enable(RccAxB::APB1, &rcc_field, false)
        };

        let arr = (1000000 / self.freq) as u16;
        let mut ch_src = quote! {};
        let mut ccmr1 = Vec::new();
        let mut ccmr2 = Vec::new();
        let mut ccer = Vec::new();
        let mut expand_utils = quote! {};

        for ch in self.ch.iter() {
            let ccr = format_ident!("ccr{}", ch.ch);
            let ccr_val = ((arr as u32) * (ch.duty as u32) / 100) as u16;
            ch_src.extend(quote! {
                pwm.#ccr().write(|w| w.ccr().variant(#ccr_val));
            });
            let ccs = format_ident!("cc{}s", ch.ch);
            let ocfe = format_ident!("oc{}fe", ch.ch);
            let ocpe = format_ident!("oc{}pe", ch.ch);
            let ocm = format_ident!("oc{}m", ch.ch);
            let occe = format_ident!("oc{}ce", ch.ch);
            let pwm_mode = format_ident!("pwm_mode{}", ch.mode);
            let ccmr = quote! {
                .#ccs().output()
                .#ocfe().clear_bit()
                .#ocpe().disabled()
                .#ocm().#pwm_mode()
                .#occe().clear_bit()
            };
            if ch.ch <= 2 {
                ccmr1.push(ccmr);
            } else {
                ccmr2.push(ccmr);
            }
            let cce = format_ident!("cc{}e", ch.ch);
            ccer.push(cce);

            if ch.set_duty {
                let fn_set_duty = format_ident!("{}_ch{}_set_duty", self.tim.name(), ch.ch);
                expand_utils.extend(quote! {
                    pub fn #fn_set_duty(duty: u8) {
                        let pwm: stm32f1::stm32f103::#name = unsafe { ::core::mem::transmute(()) };
                        let n = pwm.arr.read().arr().bits() * (duty as u16) / 100;
                        pwm.#ccr().write(|w| w.ccr().variant(n));
                    }
                });
            }
        }
        if !ccmr1.is_empty() {
            ch_src.extend(quote! {
                pwm.ccmr1_output().write(|w| w #(#ccmr1)*);
            });
        }
        if !ccmr2.is_empty() {
            ch_src.extend(quote! {
                pwm.ccmr2_output().write(|w| w #(#ccmr2)*);
            });
        }
        
        if self.enable {
            let fn_enable = format_ident!("{}_pwm_enable", self.tim.name());
            expand_utils.extend(quote! {
                pub fn #fn_enable() {
                    let pwm: stm32f1::stm32f103::#name = unsafe { ::core::mem::transmute(()) };
                    pwm.egr.write(|w| w.ug().update());
                    pwm.cr1.write(|w| w.cen().enabled());
                }
            });
        }
        if self.disable {
            let fn_disable = format_ident!("{}_pwm_disable", self.tim.name());
            expand_utils.extend(quote! {
                pub fn #fn_disable() {
                    let pwm: stm32f1::stm32f103::#name = unsafe { ::core::mem::transmute(()) };
                    pwm.cr1.reset();
                }
            });
        }
        if self.set_freq {
            let fn_set_freq = format_ident!("{}_pwm_set_freq", self.tim.name());
            let mut expand_ch = quote! {
                let ccr_val = tmp / 2;
            };
            for ch in self.ch.iter() {
                let ccr = format_ident!("ccr{}", ch.ch);
                expand_ch.extend(quote! {
                    pwm.#ccr().write(|w| w.ccr().variant(ccr_val as u16));
                });
            }
            expand_utils.extend(quote! {
                pub fn #fn_set_freq(freq: u32) {
                    let pwm: stm32f1::stm32f103::#name = unsafe { ::core::mem::transmute(()) };
                    let tmp = 1000000 / freq;
                    pwm.arr.write(|w| w.arr().variant((tmp - 1) as u16));
                    #expand_ch
                }
            });
        }
        if self.irq {
            let fn_enable_irq = format_ident!("{}_pwm_enable_irq", self.tim.name());
            let fn_disable_irq = format_ident!("{}_pwm_disable_irq", self.tim.name());
            let fn_is_irq = format_ident!("{}_is_irq", self.tim.name());
            let fn_clear_irq = format_ident!("{}_clear_irq", self.tim.name());
            expand_utils.extend(quote! {
                pub fn #fn_enable_irq() {
                    let pwm: stm32f1::stm32f103::#name = unsafe { ::core::mem::transmute(()) };
                    pwm.dier.modify(|_, w| w.uie().enabled());
                }
                pub fn #fn_disable_irq() {
                    let pwm: stm32f1::stm32f103::#name = unsafe { ::core::mem::transmute(()) };
                    pwm.dier.modify(|_, w| w.uie().disabled());
                }
                pub fn #fn_is_irq() -> bool {
                    let pwm: stm32f1::stm32f103::#name = unsafe { core::mem::transmute(()) };
                    pwm.sr.read().uif().is_update_pending()
                }
                pub fn #fn_clear_irq() {
                    let pwm: stm32f1::stm32f103::#name = unsafe { core::mem::transmute(()) };
                    pwm.sr.modify(|_, w| w.uif().clear());
                }
            });
        }
        
        quote! {

            pub fn #init_fn() {
                #rcc_src
                let pwm: stm32f1::stm32f103::#name = unsafe { ::core::mem::transmute(()) };
                pwm.cr1.reset();
                pwm.cr2.reset();
                pwm.psc.write(|w| w.psc().variant(72 - 1));
                pwm.arr.write(|w| w.arr().variant(#arr - 1));
                #ch_src
                pwm.ccer.write(|w| w #(.#ccer().set_bit())*);
            }
            #expand_utils
        }
    }
}

pub fn build(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;
    let config = PwmConfig::from_derive_input(&input).unwrap();
    let build = config.build();
    let expand = quote! {
        impl #name {
            #build
        }
    };
    expand.into()
}





